< previous page page_187 next page >

Page 187
Now, if we were using the approach shown in the GetKeyInfo2 function, where an alias was created so that the RegQueryInfoKey function was called with those parameters passed by reference, this function would work. Visual Basic would take the variant parameter, create a temporary Long variable, pass the address of the Long variable to the function, then copy the Long variable back into the variant.
But we're passing the address explicitly here, and that means the VarPtr operator is returning the address of the variant. A variant is represented internally by a structure, so VarPtr returns the address of the start of the structure. And the Long value we are interested in is not stored at the start of the variant structure. The truth is, it's amazing that this bug does not crash the system by loading invalid values into the variant. In fact, it's quite possible that there are combinations of subkey counts and value name lengths that could cause this bug to trigger a memory exception instead of just failing to work.
No Boom TodayBoom Tomorrow (There's Always a Boom Tomorrow)1
Count yourself lucky if the preceding bug did not trigger a memory exception, because as soon as you fix it, your program will start crashing wildly (at least if you're running on NT). When you step through the code, you'll find that the crash is occurring during the RegEnumValue call. This may seem odd, considering that the function is almost identical to the RegEnumKey call.
Wait, I did say "almost" identical, didn't I? Indeed.
A close look at the declarations will show an important difference. The lpcbValueName parameter in the RegEnumValue function is an LPDWORD instead of a DWORD. This means that it must be called by reference instead of by value. The lpcbValueName variable should be set to the maximum length of the lpValueName buffer. On return, the variable will contain the actual length of the string.
What happened when we passed it ByVal? Let's say the length calculated by the GetValueInfo function was 50 bytes. When the parameter is declared ByVal, the number 50 is placed on the stack. But since the RegEnumValue function expects a pointer to the length, it interprets 50 as a memory address of a Long variable that contains the actual length. When the program tries to access memory address 50, it crashes with a memory exception. Boom!2
7017a6ead0e3c4111b47a554df321e9f.gif 7017a6ead0e3c4111b47a554df321e9f.gif
1 This heading is shamelessly stolen from the TV show Babylon 5, which is, incidentally, one of the best TV shows ever made.
7017a6ead0e3c4111b47a554df321e9f.gif 7017a6ead0e3c4111b47a554df321e9f.gif
2 The Windows 95/98 version of this function detects that the address is invalid and returns an invalid parameter error (error 87) instead of raising a memory exception.

 
< previous page page_187 next page >